package com.sinosoft.dsp.persistence.businessconcept.utils;

import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.XMLGregorianCalendar;

import com.sinosoft.builder.parser.SinoParser;
import com.sinosoft.builder.parser.SinoParserType;
import com.sinosoft.builder.templates.concept.CodeHieerarchyType;
import com.sinosoft.builder.templates.concept.CodeHierarchyStructure;
import com.sinosoft.builder.templates.concept.CodeSet;
import com.sinosoft.builder.templates.concept.CodeSetType;
import com.sinosoft.builder.templates.concept.CodeType;
import com.sinosoft.builder.templates.concept.CodeValueObject;
import com.sinosoft.builder.templates.concept.CodeValueObjectRef;
import com.sinosoft.builder.templates.concept.NamingElement;
import com.sinosoft.builder.templates.concept.NamingElementStatus;
import com.sinosoft.builder.templates.concept.StructureStatusType;
import com.sinosoft.dsp.persistence.businessconcept.facade.NamingElementFacade;
import com.sinosoft.dsp.persistence.exception.SinoBusinessConceptException;
import com.sinosoft.dsp.system.utils.Const;
import com.sinosoft.dsp.system.utils.DateUtils;

/**
 * 代码集帮助类
 * 
 * @author 潘巍（PeterPan）
 * @since 2011-5-22 下午02:28:10
 * 
 */
public class CodeSetUtils {

	/**
	 * 创建一个空的代码集
	 * 
	 * @param name
	 *            代码集的名称
	 * @param naming
	 *            代码集的命名
	 * @return
	 */
	public static CodeSetType newCodeSet(String name, String naming) {
		CodeSetType cs = new CodeSet();
		cs.setId(BusinessConceptConstraintUtils.createBusinessConceptId(name));
		cs.setName(name);
		return cs;
	}

	/**
	 * 创建并添加一个具体的Code
	 * 
	 * @param cs
	 *            代码集
	 * @param name
	 *            具体代码的命名
	 * @param value
	 *            具体代码的值
	 * @return
	 */
	public static CodeType newAndAppendCode(CodeSet cs, String name,
			String value) {
		CodeType ct = new CodeType();
		// 设置ID
		ct.setId(BusinessConceptConstraintUtils.createNewCodeSetCodeId(cs));
		// 设置命名元素
		newOrChangeNamingElement(ct, name, false);
		// 创建值
		newOrChangeCodeValueObject(cs, ct, value, null);
		cs.getCode().add(ct);
		return ct;
	}

	/**
	 * 创建或者修改代码集的命名
	 * 
	 * @param cst
	 * @param newName
	 * @param isDisuse
	 *            是否将最后的命名置为废止
	 * @return
	 */
	public static NamingElementFacade newOrChangeNamingElement(CodeSetType cst,
			String newName, boolean isDisuse) {
		return newOrChangeNamingElement(cst.getNamingElement(), cst.getId(),
				newName, isDisuse);
	}

	/**
	 * 创建或者修改具体的代码的命名
	 * 
	 * @param ct
	 * @param newName
	 * @param isDisuse
	 *            是否将最后的命名置为废止
	 * @return
	 */
	public static NamingElementFacade newOrChangeNamingElement(CodeType ct,
			String newName, boolean isDisuse) {
		return newOrChangeNamingElement(ct.getNamingElement(), ct.getId(),
				newName, isDisuse);
	}

	private static NamingElementFacade newOrChangeNamingElement(
			List<NamingElement> nes, String prefix, String newName,
			boolean isDisuse) {
		int size = nes.size();
		if (isDisuse) {
			if (size > 0) {
				nes.get(size - 1).setStatus(NamingElementStatus.DISUSE);
			}
		}
		NamingElementFacade nef = new NamingElementFacade(newName);
		BusinessConceptConstraintUtils.setNamingElement(nef.getNamingElement(),
				prefix, size);
		// if (size > 0) {
		// nef.getNamingElement().setStatus(NamingElementStatus.AMENDMENT);
		// }
		nef.getNamingElement().setStatus(NamingElementStatus.CREATION);
		nef.getNamingElement()
				.setChangedTime(DateUtils.getCurrentXMLCalendar());
		nes.add(nef.getNamingElement());
		return nef;
	}

	/**
	 * 创建并添加一个具体的Code值
	 * 
	 * @param cs
	 *            代码集
	 * @param ct
	 *            具体代码对象
	 * @param value
	 *            具体代码的值
	 * @param disuseDate
	 *            最后代码值的失效时间，如果为空表示使用当前时间
	 * @return
	 */
	public static CodeValueObject newOrChangeCodeValueObject(CodeSet cs,
			CodeType ct, String value, Calendar disuseDate) {
		// 值对象
		CodeValueObject cvo = new CodeValueObject();
		cvo.setId(BusinessConceptConstraintUtils
				.createNewCodeSetCodeValueObjectId(cs));
		cvo.setValue(value);
		cs.getCodeValueObject().add(cvo);

		// 值对象引用
		int cvoRefSize = ct.getCodeValueObjectRef().size();
		XMLGregorianCalendar xml = DateUtils.getCurrentXMLCalendar();
		if (cvoRefSize > 0) {
			if (disuseDate == null) {
				ct.getCodeValueObjectRef().get(cvoRefSize - 1).setEndTime(xml);
			} else {
				ct.getCodeValueObjectRef().get(cvoRefSize - 1).setEndTime(
						DateUtils.toXMLCalendar(disuseDate));
			}
		}
		CodeValueObjectRef cvoRef = new CodeValueObjectRef();
		cvoRef.setId(BusinessConceptConstraintUtils.createRefId(ct.getId(),
				cvoRefSize));
		cvoRef.setCreateTime(xml);
		cvoRef.setRef(cvo);

		ct.getCodeValueObjectRef().add(cvoRef);

		return cvo;
	}

	/**
	 * 创建并添加一个代码层结构
	 * 
	 * @param cs
	 * @param parentNodeId
	 * @param subNodeId
	 * @param hTypeId
	 * @return
	 * @throws SinoBusinessConceptException
	 */
	public static CodeHierarchyStructure newAndAppendCodeHierarchyStructure(
			CodeSet cs, String parentNodeId, String subNodeId, String hTypeId)
			throws SinoBusinessConceptException {
		CodeHierarchyStructure chs = new CodeHierarchyStructure();
		chs.setId(BusinessConceptConstraintUtils
				.createNewCodeSetCodeHierarchyStructureId(cs));
		chs.setCodeHieerarchyTypeRef(getCodeHieerarchyType(cs, hTypeId));
		chs.setStructureStatusType(StructureStatusType.NORMAL);
		chs.setCreateTime(DateUtils.getCurrentXMLCalendar());
		CodeType pCode = getCode(cs, parentNodeId);
		if (pCode == null)
			throw new SinoBusinessConceptException(
					"创建并添加一个代码层结构出现错误，父节点不存在，父节点ID：" + parentNodeId);
		chs.setParentNode(pCode);
		CodeType sCode = getCode(cs, subNodeId);
		if (sCode == null)
			throw new SinoBusinessConceptException(
					"创建并添加一个代码层结构出现错误，子节点不存在，子节点ID：" + subNodeId);
		chs.setSubNode(sCode);
		cs.getCodeHierarchyStructure().add(chs);
		return chs;
	}

	/**
	 * 判断具体代码是否有效
	 * 
	 * @param ct
	 * @return
	 */
	public static boolean isValidCode(CodeType ct) {
		if (ct != null) {
			CodeValueObjectRef cvor = ct.getCodeValueObjectRef().get(
					ct.getCodeValueObjectRef().size() - 1);
			XMLGregorianCalendar endTime = cvor.getEndTime();
			if (endTime == null
					|| endTime.compare(DateUtils.getCurrentXMLCalendar()) > 0) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 判断代码层结构是否有效
	 * 
	 * @param chs
	 * @return
	 */
	public static boolean isValidCodeHierarchyStructure(
			CodeHierarchyStructure chs) {
		if (chs != null) {
			XMLGregorianCalendar endTime = chs.getEndTime();
			if (endTime == null
					|| endTime.compare(DateUtils.getCurrentXMLCalendar()) > 0) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 删除一个具体代码
	 * 
	 * @param cs
	 * @param cId
	 * @return
	 */
	public static CodeType deleteCode(CodeSet cs, String cId) {
		CodeType ct = getCode(cs, cId);
		Map<String, CodeType> delMap = new HashMap<String, CodeType>();
		XMLGregorianCalendar cur = DateUtils.getCurrentXMLCalendar();
		delSubCode(delMap, cs, ct, cur);
		// 删除具体代码
		for (CodeType dct : delMap.values()) {
			deleteSingleCode(dct, cur);
		}
		return ct;
	}

	private static void deleteSingleCode(CodeType dct, XMLGregorianCalendar cur) {
		int size = dct.getCodeValueObjectRef().size();
		if (dct != null && size > 0) {
			dct.getCodeValueObjectRef().get(size - 1).setEndTime(cur);
		}
	}

	private static void delSubCode(Map<String, CodeType> delMap, CodeSet cs,
			CodeType subCt, XMLGregorianCalendar cur) {
		if (!delMap.containsKey(subCt.getId())) {
			if (isValidCode(subCt)) {
				delMap.put(subCt.getId(), subCt);
				for (CodeHierarchyStructure chs : cs
						.getCodeHierarchyStructure()) {
					if (isValidCodeHierarchyStructure(chs)) {
						if (chs.getParentNode() == subCt
								|| chs.getSubNode() == subCt) {
							chs.setEndTime(cur);
						}
						if (chs.getParentNode() == subCt) {
							delSubCode(delMap, cs, chs.getSubNode(), cur);
						}
					}
				}
			}
		}
	}

	/**
	 * 删除一个代码层结构
	 * 
	 * @param cs
	 * @param hId
	 * @return
	 * @throws SinoBusinessConceptException
	 */
	public static CodeHierarchyStructure deleteCodeHierarchyStructure(
			CodeSet cs, String hId) throws SinoBusinessConceptException {
		try {
			CodeHierarchyStructure chs = getCodeHierarchyStructure(cs, hId);
			chs.setEndTime(DateUtils.getCurrentXMLCalendar());
			return chs;
		} catch (Exception e) {
			throw new SinoBusinessConceptException("删除一个代码层结构出现错误，代码层结构ID："
					+ hId);
		}
	}

	/**
	 * 将一个具体代码移动到目标代码下面
	 * 
	 * @param cs
	 * @param sourceId
	 *            源代码ID
	 * @param targetid
	 *            目标代码ID
	 * @param hTypeId
	 *            代码层类型
	 * @throws SinoBusinessConceptException
	 */
	public static void moveToCode(CodeSet cs, String sourceId, String targetId,
			String hTypeId) throws SinoBusinessConceptException {
		try {
			CodeType sCode = getCode(cs, sourceId);
			if (sCode == null)
				throw new SinoBusinessConceptException("源代码节点不存在，源代码节点ID："
						+ sourceId);
			CodeType tCode = getCode(cs, targetId);
			if (tCode == null)
				throw new SinoBusinessConceptException("目标代码节点不存在，目标代码节点ID："
						+ targetId);
			XMLGregorianCalendar cur = DateUtils.getCurrentXMLCalendar();

			// 删除源代码的作为子节点的层
			for (CodeHierarchyStructure chs : cs.getCodeHierarchyStructure()) {
				if (isValidCodeHierarchyStructure(chs)) {
					if (chs.getSubNode() == sCode) {
						if (chs.getParentNode() == tCode) {
							throw new SinoBusinessConceptException(
									"无法将源代码移动到目标代码。");
						}
						chs.setEndTime(cur);
					}
				}
			}

			// 添加源代码和目标代码的映射关系
			CodeHierarchyStructure hStruc = newAndAppendCodeHierarchyStructure(
					cs, targetId, sourceId, hTypeId);
			hStruc.setStructureStatusType(StructureStatusType.MOVE);
		} catch (SinoBusinessConceptException e) {
			throw e;
		} catch (Exception e) {
			throw new SinoBusinessConceptException(
					"将一个具体代码移动到目标代码下面出现错误，源代码ID：" + sourceId + "，目标代码ID："
							+ targetId);
		}
	}

	/**
	 * 将一个具体代码合并到目标代码下面
	 * 
	 * @param cs
	 * @param sourceId
	 * @param targetId
	 * @param hTypeId
	 * @throws SinoBusinessConceptException
	 */
	public static void mergeToCode(CodeSet cs, String sourceId,
			String targetId, String hTypeId)
			throws SinoBusinessConceptException {
		try {
			CodeType sCode = getCode(cs, sourceId);
			if (sCode == null)
				throw new SinoBusinessConceptException("源代码节点不存在，源代码节点ID："
						+ sourceId);
			CodeType tCode = getCode(cs, targetId);
			if (tCode == null)
				throw new SinoBusinessConceptException("目标代码节点不存在，目标代码节点ID："
						+ targetId);
			XMLGregorianCalendar cur = DateUtils.getCurrentXMLCalendar();

			// 删除源节点以及作为父节点的层次
			deleteSingleCode(sCode, cur);

			// 删除源节点关联的层结构，同时对于作为父节点的层结构复制一份
			for (CodeHierarchyStructure chs : cs.getCodeHierarchyStructure()) {
				if (isValidCodeHierarchyStructure(chs)) {
					if (chs.getParentNode() == sCode
							|| chs.getSubNode() == sCode) {
						chs.setEndTime(cur);
						if (chs.getParentNode() == sCode) {
							CodeHierarchyStructure hStruc = newAndAppendCodeHierarchyStructure(
									cs, targetId, chs.getSubNode().getId(),
									hTypeId);
							hStruc
									.setStructureStatusType(StructureStatusType.COMBINE);
						}
					}
				}
			}
		} catch (SinoBusinessConceptException e) {
			throw e;
		} catch (Exception e) {
			throw new SinoBusinessConceptException(
					"将一个具体代码合并到目标代码下面出现错误，源代码ID：" + sourceId + "，目标代码ID："
							+ targetId);
		}

	}

	/**
	 * 创建并添加一个代码层类型
	 * 
	 * @param cs
	 * @param name
	 * @param description
	 * @return
	 */
	public static CodeHieerarchyType newAndAppendCodeHieerarchyType(CodeSet cs,
			String name, String description) {
		CodeHieerarchyType cht = new CodeHieerarchyType();
		cht.setId(BusinessConceptConstraintUtils
				.createNewCodeSetCodeHierarchyTypeId(cs));
		cht.setName(name);
		cht.setDescription(description);
		cs.getCodeHieerarchyType().add(cht);
		return cht;
	}

	/**
	 * 通过具体代码ID获得具体代码对象
	 * 
	 * @param cs
	 * @param cTypeId
	 * @return
	 */
	public static CodeType getCode(CodeSet cs, String cTypeId) {
		if (!Const.isEmpty(cTypeId))
			for (CodeType code : cs.getCode()) {
				if (code.getId().equals(cTypeId)) {
					return code;
				}
			}
		return null;
	}

	/**
	 * 通过代码层结构类型ID获得代码层结构类型对象
	 * 
	 * @param cs
	 * @param hTypeId
	 * @return
	 */
	public static CodeHieerarchyType getCodeHieerarchyType(CodeSet cs,
			String hTypeId) {
		if (!Const.isEmpty(hTypeId))
			for (CodeHieerarchyType type : cs.getCodeHieerarchyType()) {
				if (type.getId().equals(hTypeId)) {
					return type;
				}
			}
		return null;
	}

	/**
	 * 通过代码层结构ID获得代码层结构对象
	 * 
	 * @param cs
	 * @param hTypeId
	 * @return
	 */
	public static CodeHierarchyStructure getCodeHierarchyStructure(CodeSet cs,
			String hTypeId) {
		if (!Const.isEmpty(hTypeId))
			for (CodeHierarchyStructure type : cs.getCodeHierarchyStructure()) {
				if (type.getId().equals(hTypeId)) {
					return type;
				}
			}
		return null;
	}

	/**
	 * 通过数据集对象写回xml文件
	 * 
	 * @param file
	 *            含路径的xml文件
	 * @param codeset
	 *            数据集对象
	 */
	public static void createXmlFile(File file, CodeSet codeset)
			throws IOException, JAXBException {
		SinoParser<CodeSet> parser = new SinoParser<CodeSet>();
		parser.unparse(SinoParserType.BUSSINESSCONCEPT_CODESET, codeset, file);
	}

	/**
	 * 创建数据字典目录
	 * 
	 * @param dirName
	 *            不包含路径的目录名
	 */
	public static File createXmlDir(File dir, String dirName)
			throws IOException {
		File subdir = new File(dir, dirName);
		if (subdir.exists() == false)
			subdir.mkdir();
		return subdir;

	}

	/**
	 * 获取数据字典子目录
	 * 
	 */
	public static File getCodeSetXmlDir() throws IOException {
		File dir = SinoParser
				.getXMLFile(SinoParserType.BUSSINESSCONCEPT_CODESET);
		return dir;

	}

	/**
	 * 通过xml文件获得数据集对象
	 * 
	 * @param file
	 *            xml文件
	 * @return codeset 数据集对象
	 */
	public static CodeSet getCodeSetByFile(File file) throws IOException {
		SinoParser<CodeSet> parser = new SinoParser<CodeSet>();
		Unmarshaller unmarshaller = parser
				.getUnmarshaller(SinoParserType.BUSSINESSCONCEPT_CODESET);
		CodeSet codeset = parser.parse(unmarshaller, file.getPath(), null);
		return codeset;
	}

	/**
	 * 向xml文件加具体数据集对象
	 * 
	 * @param file
	 *            xml文件
	 * @param name
	 *            具体数据集NamingElement的fullName属性
	 * @param value
	 *            具体数据集值
	 * @throws JAXBException
	 * 
	 * @return 创建的具体Code对象
	 */
	public static CodeType addCodeTypeFromFile(File file, String name,
			String value) throws IOException, JAXBException {
		if (file == null)
			return null;
		if (Const.isEmpty(name))
			return null;
		if (Const.isEmpty(value))
			return null;
		if (file.exists() == false)
			return null;
		CodeSet codeset = getCodeSetByFile(file);
		if (codeset == null)
			return null;
		// 加入新节点
		CodeType ct = newAndAppendCode(codeset, name, value);
		// 通过数据集对象写xml文件
		createXmlFile(file, codeset);
		return ct;
	}

	/**
	 * 更新xml文件具体数据集对象
	 * 
	 * @param file
	 *            xml文件
	 * @param name
	 *            具体数据集NamingElement的fullName属性
	 * @param value
	 *            具体数据集值
	 * @param codeTypeId
	 *            选中的具体数据集id
	 * @throws JAXBException
	 * 
	 * @return 更新的具体Code对象
	 */
	public static CodeType updateCodeTypeFromFile(File file, String name,
			String value, String codeTypeId) throws IOException, JAXBException {
		if (Const.isEmpty(name))
			return null;
		if (Const.isEmpty(value))
			return null;
		if (Const.isEmpty(codeTypeId))
			return null;
		if (file == null)
			return null;
		if (file.exists() == false)
			return null;

		CodeSet codeset = getCodeSetByFile(file);
		CodeType ct = getCode(codeset, codeTypeId);
		if (ct == null)
			return null;
		boolean isUpdate = false;
		// 加入新节点
		if (!isNamingElemenEquals(ct, name)) {
			isUpdate = true;
			newOrChangeNamingElement(ct, name, false);
		}
		if (!isCodeValueEquals(ct, value)) {
			isUpdate = true;
			newOrChangeCodeValueObject(codeset, ct, value, null);
		}
		if (isUpdate)
			createXmlFile(file, codeset); // 通过数据集对象写xml文件
		return ct;

	}

	/**
	 * 更新xml文件具体数据集对象
	 * 
	 * @param file
	 *            xml文件
	 *@param codeTypeId
	 *            选中的具体数据集id
	 * @throws JAXBException
	 * @throws SinoBusinessConceptException
	 * @return isDel 是否执行了删除操作
	 */
	public static boolean delCodeTypeFromFile(File file, String codeTypeId)
			throws IOException, JAXBException, SinoBusinessConceptException {
		if (file == null)
			return false;
		if (file.exists() == false)
			return false;
		if (Const.isEmpty(codeTypeId))
			return false;
		boolean isDel = false;
		CodeSet codeset = CodeSetUtils.getCodeSetByFile(file);
		if (codeset != null) {
			isDel = true;
			deleteCode(codeset, codeTypeId);
			if (isDel)
				createXmlFile(file, codeset);
		}
		return isDel;
	}

	/**
	 * 向xml文件加子具体数据集对象
	 * 
	 * @param file
	 *            xml文件
	 * @param name
	 *            具体数据集NamingElement的fullName属性
	 * @param value
	 *            具体数据集值
	 * @param codeTypeId
	 *            选中的具体数据集id
	 * @throws JAXBException
	 * @throws SinoBusinessConceptException
	 * @return 创建的具体Code对象
	 */
	public static CodeType addSubCodeTypeFromFile(File file, String name,
			String value, String codeTypeId) throws IOException, JAXBException,
			SinoBusinessConceptException {
		if (Const.isEmpty(codeTypeId))
			return null;
		if (Const.isEmpty(name))
			return null;
		if (Const.isEmpty(value))
			return null;
		if (file == null)
			return null;
		CodeSet codeset = getCodeSetByFile(file);
		CodeType ct = getCode(codeset, codeTypeId);
		if (ct == null)
			return null;
		// 加入新节点
		CodeType subct = newAndAppendCode(codeset, name, value);
		// 加入节点层
		newAndAppendCodeHierarchyStructure(codeset, ct.getId(), subct.getId(),
				null);
		// 通过数据集对象写xml文件
		createXmlFile(file, codeset);
		return ct;
	}

	/**
	 * 创建xml文件
	 * 
	 * @param file
	 *            xml文件
	 * @param name
	 *            数据集名称
	 * @param naming
	 *            数据集NamingElement的fullName属性
	 * @throws JAXBException
	 * @throws SinoBusinessConceptException
	 * @return 创建的数据集
	 */
	public static CodeSet createCodeSet(File file, String name, String naming)
			throws IOException, JAXBException, SinoBusinessConceptException {
		if (Const.isEmpty(naming))
			return null;
		if (Const.isEmpty(name))
			return null;
		CodeSet cs = (CodeSet) newCodeSet(name, null);
		NamingElementFacade nef = new NamingElementFacade(naming);
		BusinessConceptConstraintUtils.setNamingElement(nef.getNamingElement(),
				cs.getId(), cs.getNamingElement().size());
		nef.getNamingElement().setStatus(NamingElementStatus.CREATION);
		nef.getNamingElement()
				.setChangedTime(DateUtils.getCurrentXMLCalendar());
		cs.getNamingElement().add(nef.getNamingElement());
		createXmlFile(file, cs);
		return cs;
	}

	/**
	 * 代码名称是否改变
	 * 
	 * @param ct
	 *            具体代码对象
	 * @param newValue
	 *            页面中新输入的值
	 * @return
	 */

	private static boolean isNamingElemenEquals(CodeType ct, String newValue) {
		if (ct.getNamingElement().size() == 0)
			return true;
		String oldValue = ct.getNamingElement().get(
				ct.getNamingElement().size() - 1).getFullName();
		return oldValue.equalsIgnoreCase(newValue);
	}

	/**
	 * 代码值是否改变
	 * 
	 * @param ct
	 *            具体代码对象
	 * @param newValue
	 *            页面中新输入的值
	 * @return
	 */
	private static boolean isCodeValueEquals(CodeType ct, String newValue) {
		if (ct.getCodeValueObjectRef().size() == 0)
			return true;
		CodeValueObject cvo = ct.getCodeValueObjectRef().get(
				ct.getCodeValueObjectRef().size() - 1).getRef();
		if (cvo == null)
			return true;
		String oldValue = cvo.getValue();
		return oldValue.equalsIgnoreCase(newValue);
	}

}
